#     Copyright 2024, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file


# We are not avoiding these in generated code at all
# pylint: disable=I0021,line-too-long,too-many-instance-attributes,too-many-lines
# pylint: disable=I0021,too-many-arguments,too-many-return-statements,too-many-statements


"""Specialized attribute nodes

WARNING, this code is GENERATED. Modify the template AttributeNodeFixed.py.j2 instead!

spell-checker: ignore __prepare__ append args autograph capitalize casefold center chars
spell-checker: ignore clear copy count decode default delete dist distribution_name encode
spell-checker: ignore encoding end endswith errors exit_code expandtabs
spell-checker: ignore experimental_attributes experimental_autograph_options
spell-checker: ignore experimental_compile experimental_follow_type_hints
spell-checker: ignore experimental_implements experimental_relax_shapes extend fillchar
spell-checker: ignore find format format_map formatmap fromkeys func get group handle
spell-checker: ignore has_key haskey index input_signature insert isalnum isalpha isascii
spell-checker: ignore isdecimal isdigit isidentifier islower isnumeric isprintable isspace
spell-checker: ignore istitle isupper item items iterable iteritems iterkeys itervalues
spell-checker: ignore jit_compile join keepends key keys kwargs ljust lower lstrip
spell-checker: ignore maketrans maxsplit mode name new old p package
spell-checker: ignore package_or_requirement pairs partition path pop popitem prefix
spell-checker: ignore prepare reduce_retracing remove replace resource resource_name
spell-checker: ignore reverse rfind rindex rjust rpartition rsplit rstrip s sep setdefault
spell-checker: ignore sort split splitlines start startswith stop strip sub suffix
spell-checker: ignore swapcase table tabsize title translate update upper use_errno
spell-checker: ignore use_last_error value values viewitems viewkeys viewvalues width
spell-checker: ignore winmode zfill
"""


from nuitka.PythonVersions import getDictFromkeysNoArgErrorMessage
from nuitka.specs.BuiltinBytesOperationSpecs import (
    bytes_capitalize_spec,
    bytes_center_spec,
    bytes_count_spec,
    bytes_decode_spec,
    bytes_endswith_spec,
    bytes_expandtabs_spec,
    bytes_find_spec,
    bytes_index_spec,
    bytes_isalnum_spec,
    bytes_isalpha_spec,
    bytes_isdigit_spec,
    bytes_islower_spec,
    bytes_isspace_spec,
    bytes_istitle_spec,
    bytes_isupper_spec,
    bytes_join_spec,
    bytes_ljust_spec,
    bytes_lower_spec,
    bytes_lstrip_spec,
    bytes_partition_spec,
    bytes_replace_spec,
    bytes_rfind_spec,
    bytes_rindex_spec,
    bytes_rjust_spec,
    bytes_rpartition_spec,
    bytes_rsplit_spec,
    bytes_rstrip_spec,
    bytes_split_spec,
    bytes_splitlines_spec,
    bytes_startswith_spec,
    bytes_strip_spec,
    bytes_swapcase_spec,
    bytes_title_spec,
    bytes_translate_spec,
    bytes_upper_spec,
    bytes_zfill_spec,
)
from nuitka.specs.BuiltinDictOperationSpecs import (
    dict_clear_spec,
    dict_copy_spec,
    dict_fromkeys_spec,
    dict_get_spec,
    dict_has_key_spec,
    dict_items_spec,
    dict_iteritems_spec,
    dict_iterkeys_spec,
    dict_itervalues_spec,
    dict_keys_spec,
    dict_pop_spec,
    dict_popitem_spec,
    dict_setdefault_spec,
    dict_update_spec,
    dict_values_spec,
    dict_viewitems_spec,
    dict_viewkeys_spec,
    dict_viewvalues_spec,
)
from nuitka.specs.BuiltinListOperationSpecs import (
    list_append_spec,
    list_clear_spec,
    list_copy_spec,
    list_count_spec,
    list_extend_spec,
    list_index_spec,
    list_insert_spec,
    list_pop_spec,
    list_remove_spec,
    list_reverse_spec,
)
from nuitka.specs.BuiltinParameterSpecs import extractBuiltinArgs
from nuitka.specs.BuiltinStrOperationSpecs import (
    str_capitalize_spec,
    str_center_spec,
    str_count_spec,
    str_decode_spec,
    str_encode_spec,
    str_endswith_spec,
    str_expandtabs_spec,
    str_find_spec,
    str_format_spec,
    str_index_spec,
    str_isalnum_spec,
    str_isalpha_spec,
    str_isdigit_spec,
    str_islower_spec,
    str_isspace_spec,
    str_istitle_spec,
    str_isupper_spec,
    str_join_spec,
    str_ljust_spec,
    str_lower_spec,
    str_lstrip_spec,
    str_partition_spec,
    str_replace_spec,
    str_rfind_spec,
    str_rindex_spec,
    str_rjust_spec,
    str_rpartition_spec,
    str_rsplit_spec,
    str_rstrip_spec,
    str_split_spec,
    str_splitlines_spec,
    str_startswith_spec,
    str_strip_spec,
    str_swapcase_spec,
    str_title_spec,
    str_translate_spec,
    str_upper_spec,
    str_zfill_spec,
)
from nuitka.specs.BuiltinTypeOperationSpecs import type___prepare___spec

from .AttributeLookupNodes import ExpressionAttributeLookupFixedBase
from .AttributeNodes import makeExpressionAttributeLookup
from .BuiltinTypeNodes import ExpressionTypeOperationPrepare
from .BytesNodes import (
    ExpressionBytesOperationCapitalize,
    ExpressionBytesOperationCenter2,
    ExpressionBytesOperationCenter3,
    ExpressionBytesOperationCount2,
    ExpressionBytesOperationCount3,
    ExpressionBytesOperationCount4,
    ExpressionBytesOperationDecode1,
    ExpressionBytesOperationDecode2,
    ExpressionBytesOperationEndswith2,
    ExpressionBytesOperationEndswith3,
    ExpressionBytesOperationEndswith4,
    ExpressionBytesOperationExpandtabs1,
    ExpressionBytesOperationExpandtabs2,
    ExpressionBytesOperationFind2,
    ExpressionBytesOperationFind3,
    ExpressionBytesOperationFind4,
    ExpressionBytesOperationIndex2,
    ExpressionBytesOperationIndex3,
    ExpressionBytesOperationIndex4,
    ExpressionBytesOperationIsalnum,
    ExpressionBytesOperationIsalpha,
    ExpressionBytesOperationIsdigit,
    ExpressionBytesOperationIslower,
    ExpressionBytesOperationIsspace,
    ExpressionBytesOperationIstitle,
    ExpressionBytesOperationIsupper,
    ExpressionBytesOperationJoin,
    ExpressionBytesOperationLjust2,
    ExpressionBytesOperationLjust3,
    ExpressionBytesOperationLower,
    ExpressionBytesOperationLstrip1,
    ExpressionBytesOperationLstrip2,
    ExpressionBytesOperationPartition,
    ExpressionBytesOperationReplace3,
    ExpressionBytesOperationReplace4,
    ExpressionBytesOperationRfind2,
    ExpressionBytesOperationRfind3,
    ExpressionBytesOperationRfind4,
    ExpressionBytesOperationRindex2,
    ExpressionBytesOperationRindex3,
    ExpressionBytesOperationRindex4,
    ExpressionBytesOperationRjust2,
    ExpressionBytesOperationRjust3,
    ExpressionBytesOperationRpartition,
    ExpressionBytesOperationRsplit1,
    ExpressionBytesOperationRsplit2,
    ExpressionBytesOperationRstrip1,
    ExpressionBytesOperationRstrip2,
    ExpressionBytesOperationSplit1,
    ExpressionBytesOperationSplit2,
    ExpressionBytesOperationSplitlines1,
    ExpressionBytesOperationSplitlines2,
    ExpressionBytesOperationStartswith2,
    ExpressionBytesOperationStartswith3,
    ExpressionBytesOperationStartswith4,
    ExpressionBytesOperationStrip1,
    ExpressionBytesOperationStrip2,
    ExpressionBytesOperationSwapcase,
    ExpressionBytesOperationTitle,
    ExpressionBytesOperationTranslate2,
    ExpressionBytesOperationTranslate3,
    ExpressionBytesOperationUpper,
    ExpressionBytesOperationZfill,
    makeExpressionBytesOperationDecode3,
    makeExpressionBytesOperationRsplit3,
    makeExpressionBytesOperationSplit3,
)
from .ConstantRefNodes import makeConstantRefNode
from .DictionaryNodes import (
    ExpressionDictOperationClear,
    ExpressionDictOperationCopy,
    ExpressionDictOperationFromkeys2,
    ExpressionDictOperationFromkeys3,
    ExpressionDictOperationFromkeysRef,
    ExpressionDictOperationGet2,
    ExpressionDictOperationGet3,
    ExpressionDictOperationHaskey,
    ExpressionDictOperationItems,
    ExpressionDictOperationIteritems,
    ExpressionDictOperationIterkeys,
    ExpressionDictOperationItervalues,
    ExpressionDictOperationKeys,
    ExpressionDictOperationPop2,
    ExpressionDictOperationPop3,
    ExpressionDictOperationPopitem,
    ExpressionDictOperationSetdefault2,
    ExpressionDictOperationSetdefault3,
    ExpressionDictOperationUpdate2,
    ExpressionDictOperationValues,
    ExpressionDictOperationViewitems,
    ExpressionDictOperationViewkeys,
    ExpressionDictOperationViewvalues,
    makeExpressionDictOperationUpdate3,
)
from .KeyValuePairNodes import makeKeyValuePairExpressionsFromKwArgs
from .ListOperationNodes import (
    ExpressionListOperationAppend,
    ExpressionListOperationClear,
    ExpressionListOperationCopy,
    ExpressionListOperationCount,
    ExpressionListOperationExtend,
    ExpressionListOperationIndex2,
    ExpressionListOperationIndex3,
    ExpressionListOperationIndex4,
    ExpressionListOperationInsert,
    ExpressionListOperationPop1,
    ExpressionListOperationPop2,
    ExpressionListOperationRemove,
    ExpressionListOperationReverse,
)
from .NodeBases import SideEffectsFromChildrenMixin
from .NodeMakingHelpers import (
    makeRaiseExceptionReplacementExpression,
    wrapExpressionWithNodeSideEffects,
)
from .StrNodes import (
    ExpressionStrOperationCapitalize,
    ExpressionStrOperationCenter2,
    ExpressionStrOperationCenter3,
    ExpressionStrOperationCount2,
    ExpressionStrOperationCount3,
    ExpressionStrOperationCount4,
    ExpressionStrOperationDecode1,
    ExpressionStrOperationDecode2,
    ExpressionStrOperationDecode3,
    ExpressionStrOperationEncode1,
    ExpressionStrOperationEncode2,
    ExpressionStrOperationEndswith2,
    ExpressionStrOperationEndswith3,
    ExpressionStrOperationEndswith4,
    ExpressionStrOperationExpandtabs1,
    ExpressionStrOperationExpandtabs2,
    ExpressionStrOperationFind2,
    ExpressionStrOperationFind3,
    ExpressionStrOperationFind4,
    ExpressionStrOperationFormat,
    ExpressionStrOperationIndex2,
    ExpressionStrOperationIndex3,
    ExpressionStrOperationIndex4,
    ExpressionStrOperationIsalnum,
    ExpressionStrOperationIsalpha,
    ExpressionStrOperationIsdigit,
    ExpressionStrOperationIslower,
    ExpressionStrOperationIsspace,
    ExpressionStrOperationIstitle,
    ExpressionStrOperationIsupper,
    ExpressionStrOperationJoin,
    ExpressionStrOperationLjust2,
    ExpressionStrOperationLjust3,
    ExpressionStrOperationLower,
    ExpressionStrOperationLstrip1,
    ExpressionStrOperationLstrip2,
    ExpressionStrOperationPartition,
    ExpressionStrOperationReplace3,
    ExpressionStrOperationReplace4,
    ExpressionStrOperationRfind2,
    ExpressionStrOperationRfind3,
    ExpressionStrOperationRfind4,
    ExpressionStrOperationRindex2,
    ExpressionStrOperationRindex3,
    ExpressionStrOperationRindex4,
    ExpressionStrOperationRjust2,
    ExpressionStrOperationRjust3,
    ExpressionStrOperationRpartition,
    ExpressionStrOperationRsplit1,
    ExpressionStrOperationRsplit2,
    ExpressionStrOperationRstrip1,
    ExpressionStrOperationRstrip2,
    ExpressionStrOperationSplit1,
    ExpressionStrOperationSplit2,
    ExpressionStrOperationSplitlines1,
    ExpressionStrOperationSplitlines2,
    ExpressionStrOperationStartswith2,
    ExpressionStrOperationStartswith3,
    ExpressionStrOperationStartswith4,
    ExpressionStrOperationStrip1,
    ExpressionStrOperationStrip2,
    ExpressionStrOperationSwapcase,
    ExpressionStrOperationTitle,
    ExpressionStrOperationTranslate,
    ExpressionStrOperationUpper,
    ExpressionStrOperationZfill,
    makeExpressionStrOperationEncode3,
    makeExpressionStrOperationRsplit3,
    makeExpressionStrOperationSplit3,
)

attribute_classes = {}
attribute_typed_classes = set()


class ExpressionAttributeLookupFixedPrepare(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value '__prepare__' of an object.

    Typically code like: source.__prepare__
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_PREPARE"
    attribute_name = "__prepare__"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is not bytes and subnode_expression.hasShapeTypeExact():
            result = ExpressionAttributeLookupTypePrepare(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup '__prepare__' on type shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="__prepare__",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="__prepare__"
        )


attribute_classes["__prepare__"] = ExpressionAttributeLookupFixedPrepare


class ExpressionAttributeLookupTypePrepare(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedPrepare
):
    """Attribute '__prepare__' lookup on a type value.

    Typically code like: some_type.__prepare__
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_TYPE_PREPARE"
    attribute_name = "__prepare__"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the type.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, type_arg, trace_collection):
        def wrapExpressionTypeOperationPrepare(args, kwargs, source_ref):
            return ExpressionTypeOperationPrepare(
                type_arg=type_arg, args=args, kwargs=kwargs, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionTypeOperationPrepare,
            builtin_spec=type___prepare___spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to '__prepare__' of type recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        type_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, type_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupTypePrepare)


class ExpressionAttributeLookupFixedAppend(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'append' of an object.

    Typically code like: source.append
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_APPEND"
    attribute_name = "append"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListAppend(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'append' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="append",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="append"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["append"] = ExpressionAttributeLookupFixedAppend


class ExpressionAttributeLookupListAppend(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedAppend
):
    """Attribute append lookup on a list value.

    Typically code like: some_list.append
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_APPEND"
    attribute_name = "append"

    def computeExpression(self, trace_collection):
        # Might be used to modify the list.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationAppend(item, source_ref):
            return ExpressionListOperationAppend(
                list_arg=list_arg, item=item, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationAppend,
            builtin_spec=list_append_spec,
        )

        return result, "new_expression", "Call to 'append' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListAppend)


class ExpressionAttributeLookupFixedCapitalize(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'capitalize' of an object.

    Typically code like: source.capitalize
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_CAPITALIZE"
    attribute_name = "capitalize"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrCapitalize(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'capitalize' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesCapitalize(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'capitalize' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="capitalize",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="capitalize"
        )


attribute_classes["capitalize"] = ExpressionAttributeLookupFixedCapitalize


class ExpressionAttributeLookupStrCapitalize(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCapitalize
):
    """Attribute 'capitalize' lookup on a str value.

    Typically code like: some_str.capitalize
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_CAPITALIZE"
    attribute_name = "capitalize"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationCapitalize(source_ref):
            return ExpressionStrOperationCapitalize(
                str_arg=str_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationCapitalize,
            builtin_spec=str_capitalize_spec,
        )

        return result, "new_expression", "Call to 'capitalize' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrCapitalize)


class ExpressionAttributeLookupBytesCapitalize(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCapitalize
):
    """Attribute 'capitalize' lookup on a bytes value.

    Typically code like: some_bytes.capitalize
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_CAPITALIZE"
    attribute_name = "capitalize"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationCapitalize(source_ref):
            return ExpressionBytesOperationCapitalize(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationCapitalize,
            builtin_spec=bytes_capitalize_spec,
        )

        return result, "new_expression", "Call to 'capitalize' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesCapitalize)


class ExpressionAttributeLookupFixedCasefold(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'casefold' of an object.

    Typically code like: source.casefold
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_CASEFOLD"
    attribute_name = "casefold"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is not bytes and subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrCasefold(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'casefold' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="casefold",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="casefold"
        )


attribute_classes["casefold"] = ExpressionAttributeLookupFixedCasefold


class ExpressionAttributeLookupStrCasefold(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCasefold
):
    """Attribute 'casefold' lookup on a str value.

    Typically code like: some_str.casefold
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_CASEFOLD"
    attribute_name = "casefold"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    # No computeExpressionCall as str operation ExpressionStrOperationCasefold is not yet implemented


attribute_typed_classes.add(ExpressionAttributeLookupStrCasefold)


class ExpressionAttributeLookupFixedCenter(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'center' of an object.

    Typically code like: source.center
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_CENTER"
    attribute_name = "center"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrCenter(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'center' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesCenter(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'center' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="center",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="center"
        )


attribute_classes["center"] = ExpressionAttributeLookupFixedCenter


class ExpressionAttributeLookupStrCenter(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCenter
):
    """Attribute 'center' lookup on a str value.

    Typically code like: some_str.center
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_CENTER"
    attribute_name = "center"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationCenter(width, fillchar, source_ref):
            if fillchar is not None:
                return ExpressionStrOperationCenter3(
                    str_arg=str_arg,
                    width=width,
                    fillchar=fillchar,
                    source_ref=source_ref,
                )
            else:
                return ExpressionStrOperationCenter2(
                    str_arg=str_arg, width=width, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationCenter,
            builtin_spec=str_center_spec,
        )

        return result, "new_expression", "Call to 'center' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrCenter)


class ExpressionAttributeLookupBytesCenter(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCenter
):
    """Attribute 'center' lookup on a bytes value.

    Typically code like: some_bytes.center
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_CENTER"
    attribute_name = "center"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationCenter(width, fillchar, source_ref):
            if fillchar is not None:
                return ExpressionBytesOperationCenter3(
                    bytes_arg=bytes_arg,
                    width=width,
                    fillchar=fillchar,
                    source_ref=source_ref,
                )
            else:
                return ExpressionBytesOperationCenter2(
                    bytes_arg=bytes_arg, width=width, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationCenter,
            builtin_spec=bytes_center_spec,
        )

        return result, "new_expression", "Call to 'center' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesCenter)


class ExpressionAttributeLookupFixedClear(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'clear' of an object.

    Typically code like: source.clear
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_CLEAR"
    attribute_name = "clear"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictClear(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'clear' on dict shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListClear(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'clear' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="clear",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="clear"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["clear"] = ExpressionAttributeLookupFixedClear


class ExpressionAttributeLookupDictClear(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedClear
):
    """Attribute 'clear' lookup on a dict value.

    Typically code like: some_dict.clear
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_CLEAR"
    attribute_name = "clear"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Might be used to modify the dict.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationClear(source_ref):
            return ExpressionDictOperationClear(
                dict_arg=dict_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationClear,
            builtin_spec=dict_clear_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'clear' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictClear)


class ExpressionAttributeLookupListClear(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedClear
):
    """Attribute clear lookup on a list value.

    Typically code like: some_list.clear
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_CLEAR"
    attribute_name = "clear"

    def computeExpression(self, trace_collection):
        # Might be used to modify the list.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationClear(source_ref):
            return ExpressionListOperationClear(
                list_arg=list_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationClear,
            builtin_spec=list_clear_spec,
        )

        return result, "new_expression", "Call to 'clear' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListClear)


class ExpressionAttributeLookupFixedCopy(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'copy' of an object.

    Typically code like: source.copy
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_COPY"
    attribute_name = "copy"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictCopy(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'copy' on dict shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListCopy(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'copy' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="copy",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="copy"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["copy"] = ExpressionAttributeLookupFixedCopy


class ExpressionAttributeLookupDictCopy(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCopy
):
    """Attribute 'copy' lookup on a dict value.

    Typically code like: some_dict.copy
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_COPY"
    attribute_name = "copy"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Might be used to modify the dict.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationCopy(source_ref):
            return ExpressionDictOperationCopy(dict_arg=dict_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationCopy,
            builtin_spec=dict_copy_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'copy' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictCopy)


class ExpressionAttributeLookupListCopy(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCopy
):
    """Attribute copy lookup on a list value.

    Typically code like: some_list.copy
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_COPY"
    attribute_name = "copy"

    def computeExpression(self, trace_collection):
        # Might be used to modify the list.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationCopy(source_ref):
            return ExpressionListOperationCopy(list_arg=list_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationCopy,
            builtin_spec=list_copy_spec,
        )

        return result, "new_expression", "Call to 'copy' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListCopy)


class ExpressionAttributeLookupFixedCount(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'count' of an object.

    Typically code like: source.count
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_COUNT"
    attribute_name = "count"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrCount(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'count' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesCount(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'count' on bytes shape resolved.",
            )
        if subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListCount(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'count' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="count",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="count"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["count"] = ExpressionAttributeLookupFixedCount


class ExpressionAttributeLookupStrCount(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCount
):
    """Attribute 'count' lookup on a str value.

    Typically code like: some_str.count
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_COUNT"
    attribute_name = "count"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationCount(sub, start, end, source_ref):
            if end is not None:
                return ExpressionStrOperationCount4(
                    str_arg=str_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionStrOperationCount3(
                    str_arg=str_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationCount2(
                    str_arg=str_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationCount,
            builtin_spec=str_count_spec,
        )

        return result, "new_expression", "Call to 'count' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrCount)


class ExpressionAttributeLookupBytesCount(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCount
):
    """Attribute 'count' lookup on a bytes value.

    Typically code like: some_bytes.count
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_COUNT"
    attribute_name = "count"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationCount(sub, start, end, source_ref):
            if end is not None:
                return ExpressionBytesOperationCount4(
                    bytes_arg=bytes_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionBytesOperationCount3(
                    bytes_arg=bytes_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationCount2(
                    bytes_arg=bytes_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationCount,
            builtin_spec=bytes_count_spec,
        )

        return result, "new_expression", "Call to 'count' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesCount)


class ExpressionAttributeLookupListCount(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedCount
):
    """Attribute count lookup on a list value.

    Typically code like: some_list.count
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_COUNT"
    attribute_name = "count"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the list.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationCount(value, source_ref):
            return ExpressionListOperationCount(
                list_arg=list_arg, value=value, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationCount,
            builtin_spec=list_count_spec,
        )

        return result, "new_expression", "Call to 'count' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListCount)


class ExpressionAttributeLookupFixedDecode(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'decode' of an object.

    Typically code like: source.decode
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_DECODE"
    attribute_name = "decode"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is bytes and subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrDecode(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'decode' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesDecode(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'decode' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="decode",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="decode"
        )


attribute_classes["decode"] = ExpressionAttributeLookupFixedDecode


class ExpressionAttributeLookupStrDecode(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedDecode
):
    """Attribute 'decode' lookup on a str value.

    Typically code like: some_str.decode
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_DECODE"
    attribute_name = "decode"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationDecode(encoding, errors, source_ref):
            if errors is not None:
                return ExpressionStrOperationDecode3(
                    str_arg=str_arg,
                    encoding=encoding,
                    errors=errors,
                    source_ref=source_ref,
                )
            elif encoding is not None:
                return ExpressionStrOperationDecode2(
                    str_arg=str_arg, encoding=encoding, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationDecode1(
                    str_arg=str_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationDecode,
            builtin_spec=str_decode_spec,
        )

        return result, "new_expression", "Call to 'decode' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrDecode)


class ExpressionAttributeLookupBytesDecode(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedDecode
):
    """Attribute 'decode' lookup on a bytes value.

    Typically code like: some_bytes.decode
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_DECODE"
    attribute_name = "decode"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationDecode(encoding, errors, source_ref):
            if errors is not None:
                return makeExpressionBytesOperationDecode3(
                    bytes_arg=bytes_arg,
                    encoding=encoding,
                    errors=errors,
                    source_ref=source_ref,
                )
            elif encoding is not None:
                return ExpressionBytesOperationDecode2(
                    bytes_arg=bytes_arg, encoding=encoding, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationDecode1(
                    bytes_arg=bytes_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationDecode,
            builtin_spec=bytes_decode_spec,
        )

        return result, "new_expression", "Call to 'decode' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesDecode)


class ExpressionAttributeLookupFixedEncode(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'encode' of an object.

    Typically code like: source.encode
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ENCODE"
    attribute_name = "encode"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrEncode(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'encode' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="encode",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="encode"
        )


attribute_classes["encode"] = ExpressionAttributeLookupFixedEncode


class ExpressionAttributeLookupStrEncode(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedEncode
):
    """Attribute 'encode' lookup on a str value.

    Typically code like: some_str.encode
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ENCODE"
    attribute_name = "encode"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationEncode(encoding, errors, source_ref):
            if errors is not None:
                return makeExpressionStrOperationEncode3(
                    str_arg=str_arg,
                    encoding=encoding,
                    errors=errors,
                    source_ref=source_ref,
                )
            elif encoding is not None:
                return ExpressionStrOperationEncode2(
                    str_arg=str_arg, encoding=encoding, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationEncode1(
                    str_arg=str_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationEncode,
            builtin_spec=str_encode_spec,
        )

        return result, "new_expression", "Call to 'encode' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrEncode)


class ExpressionAttributeLookupFixedEndswith(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'endswith' of an object.

    Typically code like: source.endswith
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ENDSWITH"
    attribute_name = "endswith"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrEndswith(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'endswith' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesEndswith(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'endswith' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="endswith",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="endswith"
        )


attribute_classes["endswith"] = ExpressionAttributeLookupFixedEndswith


class ExpressionAttributeLookupStrEndswith(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedEndswith
):
    """Attribute 'endswith' lookup on a str value.

    Typically code like: some_str.endswith
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ENDSWITH"
    attribute_name = "endswith"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationEndswith(suffix, start, end, source_ref):
            if end is not None:
                return ExpressionStrOperationEndswith4(
                    str_arg=str_arg,
                    suffix=suffix,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionStrOperationEndswith3(
                    str_arg=str_arg, suffix=suffix, start=start, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationEndswith2(
                    str_arg=str_arg, suffix=suffix, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationEndswith,
            builtin_spec=str_endswith_spec,
        )

        return result, "new_expression", "Call to 'endswith' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrEndswith)


class ExpressionAttributeLookupBytesEndswith(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedEndswith
):
    """Attribute 'endswith' lookup on a bytes value.

    Typically code like: some_bytes.endswith
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_ENDSWITH"
    attribute_name = "endswith"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationEndswith(suffix, start, end, source_ref):
            if end is not None:
                return ExpressionBytesOperationEndswith4(
                    bytes_arg=bytes_arg,
                    suffix=suffix,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionBytesOperationEndswith3(
                    bytes_arg=bytes_arg,
                    suffix=suffix,
                    start=start,
                    source_ref=source_ref,
                )
            else:
                return ExpressionBytesOperationEndswith2(
                    bytes_arg=bytes_arg, suffix=suffix, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationEndswith,
            builtin_spec=bytes_endswith_spec,
        )

        return result, "new_expression", "Call to 'endswith' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesEndswith)


class ExpressionAttributeLookupFixedExpandtabs(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'expandtabs' of an object.

    Typically code like: source.expandtabs
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_EXPANDTABS"
    attribute_name = "expandtabs"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrExpandtabs(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'expandtabs' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesExpandtabs(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'expandtabs' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="expandtabs",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="expandtabs"
        )


attribute_classes["expandtabs"] = ExpressionAttributeLookupFixedExpandtabs


class ExpressionAttributeLookupStrExpandtabs(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedExpandtabs
):
    """Attribute 'expandtabs' lookup on a str value.

    Typically code like: some_str.expandtabs
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_EXPANDTABS"
    attribute_name = "expandtabs"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationExpandtabs(tabsize, source_ref):
            if tabsize is not None:
                return ExpressionStrOperationExpandtabs2(
                    str_arg=str_arg, tabsize=tabsize, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationExpandtabs1(
                    str_arg=str_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationExpandtabs,
            builtin_spec=str_expandtabs_spec,
        )

        return result, "new_expression", "Call to 'expandtabs' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrExpandtabs)


class ExpressionAttributeLookupBytesExpandtabs(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedExpandtabs
):
    """Attribute 'expandtabs' lookup on a bytes value.

    Typically code like: some_bytes.expandtabs
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_EXPANDTABS"
    attribute_name = "expandtabs"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationExpandtabs(tabsize, source_ref):
            if tabsize is not None:
                return ExpressionBytesOperationExpandtabs2(
                    bytes_arg=bytes_arg, tabsize=tabsize, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationExpandtabs1(
                    bytes_arg=bytes_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationExpandtabs,
            builtin_spec=bytes_expandtabs_spec,
        )

        return result, "new_expression", "Call to 'expandtabs' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesExpandtabs)


class ExpressionAttributeLookupFixedExtend(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'extend' of an object.

    Typically code like: source.extend
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_EXTEND"
    attribute_name = "extend"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListExtend(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'extend' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="extend",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="extend"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["extend"] = ExpressionAttributeLookupFixedExtend


class ExpressionAttributeLookupListExtend(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedExtend
):
    """Attribute extend lookup on a list value.

    Typically code like: some_list.extend
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_EXTEND"
    attribute_name = "extend"

    def computeExpression(self, trace_collection):
        # Might be used to modify the list.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationExtend(value, source_ref):
            return ExpressionListOperationExtend(
                list_arg=list_arg, value=value, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationExtend,
            builtin_spec=list_extend_spec,
        )

        return result, "new_expression", "Call to 'extend' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListExtend)


class ExpressionAttributeLookupFixedFind(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'find' of an object.

    Typically code like: source.find
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_FIND"
    attribute_name = "find"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrFind(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'find' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesFind(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'find' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="find",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="find"
        )


attribute_classes["find"] = ExpressionAttributeLookupFixedFind


class ExpressionAttributeLookupStrFind(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedFind
):
    """Attribute 'find' lookup on a str value.

    Typically code like: some_str.find
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_FIND"
    attribute_name = "find"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationFind(sub, start, end, source_ref):
            if end is not None:
                return ExpressionStrOperationFind4(
                    str_arg=str_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionStrOperationFind3(
                    str_arg=str_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationFind2(
                    str_arg=str_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationFind,
            builtin_spec=str_find_spec,
        )

        return result, "new_expression", "Call to 'find' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrFind)


class ExpressionAttributeLookupBytesFind(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedFind
):
    """Attribute 'find' lookup on a bytes value.

    Typically code like: some_bytes.find
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_FIND"
    attribute_name = "find"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationFind(sub, start, end, source_ref):
            if end is not None:
                return ExpressionBytesOperationFind4(
                    bytes_arg=bytes_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionBytesOperationFind3(
                    bytes_arg=bytes_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationFind2(
                    bytes_arg=bytes_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationFind,
            builtin_spec=bytes_find_spec,
        )

        return result, "new_expression", "Call to 'find' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesFind)


class ExpressionAttributeLookupFixedFormat(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'format' of an object.

    Typically code like: source.format
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_FORMAT"
    attribute_name = "format"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrFormat(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'format' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="format",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="format"
        )


attribute_classes["format"] = ExpressionAttributeLookupFixedFormat


class ExpressionAttributeLookupStrFormat(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedFormat
):
    """Attribute 'format' lookup on a str value.

    Typically code like: some_str.format
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_FORMAT"
    attribute_name = "format"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationFormat(args, pairs, source_ref):
            return ExpressionStrOperationFormat(
                str_arg=str_arg,
                args=args,
                pairs=makeKeyValuePairExpressionsFromKwArgs(pairs),
                source_ref=source_ref,
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationFormat,
            builtin_spec=str_format_spec,
        )

        return result, "new_expression", "Call to 'format' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrFormat)


class ExpressionAttributeLookupFixedFormatmap(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'format_map' of an object.

    Typically code like: source.format_map
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_FORMATMAP"
    attribute_name = "format_map"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is not bytes and subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrFormatmap(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'format_map' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="format_map",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="format_map"
        )


attribute_classes["format_map"] = ExpressionAttributeLookupFixedFormatmap


class ExpressionAttributeLookupStrFormatmap(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedFormatmap
):
    """Attribute 'format_map' lookup on a str value.

    Typically code like: some_str.format_map
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_FORMATMAP"
    attribute_name = "format_map"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    # No computeExpressionCall as str operation ExpressionStrOperationFormatmap is not yet implemented


attribute_typed_classes.add(ExpressionAttributeLookupStrFormatmap)


class ExpressionAttributeLookupFixedFromkeys(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'fromkeys' of an object.

    Typically code like: source.fromkeys
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_FROMKEYS"
    attribute_name = "fromkeys"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.isExpressionConstantTypeDictRef():
            return (
                ExpressionDictOperationFromkeysRef(source_ref=self.source_ref),
                "new_expression",
                "Reference to 'dict.fromkeys' resolved.",
            )
        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictFromkeys(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'fromkeys' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="fromkeys",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="fromkeys"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["fromkeys"] = ExpressionAttributeLookupFixedFromkeys


class ExpressionAttributeLookupDictFromkeys(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedFromkeys
):
    """Attribute 'fromkeys' lookup on a dict value.

    Typically code like: some_dict.fromkeys
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_FROMKEYS"
    attribute_name = "fromkeys"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationFromkeys(iterable, value, source_ref):
            if value is not None:
                return ExpressionDictOperationFromkeys3(
                    iterable=iterable, value=value, source_ref=source_ref
                )
            else:
                return ExpressionDictOperationFromkeys2(
                    iterable=iterable, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationFromkeys,
            builtin_spec=dict_fromkeys_spec,
            empty_special_class=lambda source_ref: makeRaiseExceptionReplacementExpression(
                expression=dict_arg,
                exception_type="TypeError",
                exception_value=getDictFromkeysNoArgErrorMessage(),
            ),
        )

        result = wrapExpressionWithNodeSideEffects(old_node=dict_arg, new_node=result)

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'fromkeys' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictFromkeys)


class ExpressionAttributeLookupFixedGet(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'get' of an object.

    Typically code like: source.get
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_GET"
    attribute_name = "get"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictGet(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'get' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="get",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="get"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["get"] = ExpressionAttributeLookupFixedGet


class ExpressionAttributeLookupDictGet(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedGet
):
    """Attribute 'get' lookup on a dict value.

    Typically code like: some_dict.get
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_GET"
    attribute_name = "get"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationGet(key, default, source_ref):
            if default is not None:
                return ExpressionDictOperationGet3(
                    dict_arg=dict_arg, key=key, default=default, source_ref=source_ref
                )
            else:
                return ExpressionDictOperationGet2(
                    dict_arg=dict_arg, key=key, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationGet,
            builtin_spec=dict_get_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'get' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictGet)


class ExpressionAttributeLookupFixedHaskey(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'has_key' of an object.

    Typically code like: source.has_key
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_HASKEY"
    attribute_name = "has_key"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is bytes and subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictHaskey(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'has_key' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="has_key",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="has_key"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["has_key"] = ExpressionAttributeLookupFixedHaskey


class ExpressionAttributeLookupDictHaskey(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedHaskey
):
    """Attribute 'has_key' lookup on a dict value.

    Typically code like: some_dict.has_key
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_HASKEY"
    attribute_name = "has_key"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationHaskey(key, source_ref):
            return ExpressionDictOperationHaskey(
                dict_arg=dict_arg, key=key, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationHaskey,
            builtin_spec=dict_has_key_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'has_key' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictHaskey)


class ExpressionAttributeLookupFixedIndex(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'index' of an object.

    Typically code like: source.index
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_INDEX"
    attribute_name = "index"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIndex(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'index' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesIndex(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'index' on bytes shape resolved.",
            )
        if subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListIndex(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'index' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="index",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="index"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["index"] = ExpressionAttributeLookupFixedIndex


class ExpressionAttributeLookupStrIndex(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIndex
):
    """Attribute 'index' lookup on a str value.

    Typically code like: some_str.index
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_INDEX"
    attribute_name = "index"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationIndex(sub, start, end, source_ref):
            if end is not None:
                return ExpressionStrOperationIndex4(
                    str_arg=str_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionStrOperationIndex3(
                    str_arg=str_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationIndex2(
                    str_arg=str_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationIndex,
            builtin_spec=str_index_spec,
        )

        return result, "new_expression", "Call to 'index' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrIndex)


class ExpressionAttributeLookupBytesIndex(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIndex
):
    """Attribute 'index' lookup on a bytes value.

    Typically code like: some_bytes.index
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_INDEX"
    attribute_name = "index"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationIndex(sub, start, end, source_ref):
            if end is not None:
                return ExpressionBytesOperationIndex4(
                    bytes_arg=bytes_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionBytesOperationIndex3(
                    bytes_arg=bytes_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationIndex2(
                    bytes_arg=bytes_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationIndex,
            builtin_spec=bytes_index_spec,
        )

        return result, "new_expression", "Call to 'index' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesIndex)


class ExpressionAttributeLookupListIndex(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIndex
):
    """Attribute index lookup on a list value.

    Typically code like: some_list.index
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_INDEX"
    attribute_name = "index"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the list.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationIndex(value, start, stop, source_ref):
            if stop is not None:
                return ExpressionListOperationIndex4(
                    list_arg=list_arg,
                    value=value,
                    start=start,
                    stop=stop,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionListOperationIndex3(
                    list_arg=list_arg, value=value, start=start, source_ref=source_ref
                )
            else:
                return ExpressionListOperationIndex2(
                    list_arg=list_arg, value=value, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationIndex,
            builtin_spec=list_index_spec,
        )

        return result, "new_expression", "Call to 'index' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListIndex)


class ExpressionAttributeLookupFixedInsert(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'insert' of an object.

    Typically code like: source.insert
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_INSERT"
    attribute_name = "insert"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListInsert(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'insert' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="insert",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="insert"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["insert"] = ExpressionAttributeLookupFixedInsert


class ExpressionAttributeLookupListInsert(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedInsert
):
    """Attribute insert lookup on a list value.

    Typically code like: some_list.insert
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_INSERT"
    attribute_name = "insert"

    def computeExpression(self, trace_collection):
        # Might be used to modify the list.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationInsert(index, item, source_ref):
            return ExpressionListOperationInsert(
                list_arg=list_arg, index=index, item=item, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationInsert,
            builtin_spec=list_insert_spec,
        )

        return result, "new_expression", "Call to 'insert' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListInsert)


class ExpressionAttributeLookupFixedIsalnum(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isalnum' of an object.

    Typically code like: source.isalnum
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISALNUM"
    attribute_name = "isalnum"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsalnum(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isalnum' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesIsalnum(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isalnum' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isalnum",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isalnum"
        )


attribute_classes["isalnum"] = ExpressionAttributeLookupFixedIsalnum


class ExpressionAttributeLookupStrIsalnum(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsalnum
):
    """Attribute 'isalnum' lookup on a str value.

    Typically code like: some_str.isalnum
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISALNUM"
    attribute_name = "isalnum"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationIsalnum(source_ref):
            return ExpressionStrOperationIsalnum(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationIsalnum,
            builtin_spec=str_isalnum_spec,
        )

        return result, "new_expression", "Call to 'isalnum' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrIsalnum)


class ExpressionAttributeLookupBytesIsalnum(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsalnum
):
    """Attribute 'isalnum' lookup on a bytes value.

    Typically code like: some_bytes.isalnum
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_ISALNUM"
    attribute_name = "isalnum"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationIsalnum(source_ref):
            return ExpressionBytesOperationIsalnum(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationIsalnum,
            builtin_spec=bytes_isalnum_spec,
        )

        return result, "new_expression", "Call to 'isalnum' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesIsalnum)


class ExpressionAttributeLookupFixedIsalpha(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isalpha' of an object.

    Typically code like: source.isalpha
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISALPHA"
    attribute_name = "isalpha"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsalpha(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isalpha' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesIsalpha(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isalpha' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isalpha",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isalpha"
        )


attribute_classes["isalpha"] = ExpressionAttributeLookupFixedIsalpha


class ExpressionAttributeLookupStrIsalpha(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsalpha
):
    """Attribute 'isalpha' lookup on a str value.

    Typically code like: some_str.isalpha
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISALPHA"
    attribute_name = "isalpha"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationIsalpha(source_ref):
            return ExpressionStrOperationIsalpha(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationIsalpha,
            builtin_spec=str_isalpha_spec,
        )

        return result, "new_expression", "Call to 'isalpha' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrIsalpha)


class ExpressionAttributeLookupBytesIsalpha(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsalpha
):
    """Attribute 'isalpha' lookup on a bytes value.

    Typically code like: some_bytes.isalpha
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_ISALPHA"
    attribute_name = "isalpha"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationIsalpha(source_ref):
            return ExpressionBytesOperationIsalpha(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationIsalpha,
            builtin_spec=bytes_isalpha_spec,
        )

        return result, "new_expression", "Call to 'isalpha' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesIsalpha)


class ExpressionAttributeLookupFixedIsascii(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isascii' of an object.

    Typically code like: source.isascii
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISASCII"
    attribute_name = "isascii"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is not bytes and subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsascii(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isascii' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isascii",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isascii"
        )


attribute_classes["isascii"] = ExpressionAttributeLookupFixedIsascii


class ExpressionAttributeLookupStrIsascii(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsascii
):
    """Attribute 'isascii' lookup on a str value.

    Typically code like: some_str.isascii
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISASCII"
    attribute_name = "isascii"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    # No computeExpressionCall as str operation ExpressionStrOperationIsascii is not yet implemented


attribute_typed_classes.add(ExpressionAttributeLookupStrIsascii)


class ExpressionAttributeLookupFixedIsdecimal(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isdecimal' of an object.

    Typically code like: source.isdecimal
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISDECIMAL"
    attribute_name = "isdecimal"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is not bytes and subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsdecimal(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isdecimal' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isdecimal",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isdecimal"
        )


attribute_classes["isdecimal"] = ExpressionAttributeLookupFixedIsdecimal


class ExpressionAttributeLookupStrIsdecimal(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsdecimal
):
    """Attribute 'isdecimal' lookup on a str value.

    Typically code like: some_str.isdecimal
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISDECIMAL"
    attribute_name = "isdecimal"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    # No computeExpressionCall as str operation ExpressionStrOperationIsdecimal is not yet implemented


attribute_typed_classes.add(ExpressionAttributeLookupStrIsdecimal)


class ExpressionAttributeLookupFixedIsdigit(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isdigit' of an object.

    Typically code like: source.isdigit
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISDIGIT"
    attribute_name = "isdigit"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsdigit(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isdigit' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesIsdigit(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isdigit' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isdigit",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isdigit"
        )


attribute_classes["isdigit"] = ExpressionAttributeLookupFixedIsdigit


class ExpressionAttributeLookupStrIsdigit(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsdigit
):
    """Attribute 'isdigit' lookup on a str value.

    Typically code like: some_str.isdigit
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISDIGIT"
    attribute_name = "isdigit"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationIsdigit(source_ref):
            return ExpressionStrOperationIsdigit(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationIsdigit,
            builtin_spec=str_isdigit_spec,
        )

        return result, "new_expression", "Call to 'isdigit' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrIsdigit)


class ExpressionAttributeLookupBytesIsdigit(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsdigit
):
    """Attribute 'isdigit' lookup on a bytes value.

    Typically code like: some_bytes.isdigit
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_ISDIGIT"
    attribute_name = "isdigit"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationIsdigit(source_ref):
            return ExpressionBytesOperationIsdigit(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationIsdigit,
            builtin_spec=bytes_isdigit_spec,
        )

        return result, "new_expression", "Call to 'isdigit' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesIsdigit)


class ExpressionAttributeLookupFixedIsidentifier(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isidentifier' of an object.

    Typically code like: source.isidentifier
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISIDENTIFIER"
    attribute_name = "isidentifier"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is not bytes and subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsidentifier(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isidentifier' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isidentifier",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isidentifier"
        )


attribute_classes["isidentifier"] = ExpressionAttributeLookupFixedIsidentifier


class ExpressionAttributeLookupStrIsidentifier(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsidentifier
):
    """Attribute 'isidentifier' lookup on a str value.

    Typically code like: some_str.isidentifier
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISIDENTIFIER"
    attribute_name = "isidentifier"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    # No computeExpressionCall as str operation ExpressionStrOperationIsidentifier is not yet implemented


attribute_typed_classes.add(ExpressionAttributeLookupStrIsidentifier)


class ExpressionAttributeLookupFixedIslower(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'islower' of an object.

    Typically code like: source.islower
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISLOWER"
    attribute_name = "islower"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIslower(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'islower' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesIslower(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'islower' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="islower",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="islower"
        )


attribute_classes["islower"] = ExpressionAttributeLookupFixedIslower


class ExpressionAttributeLookupStrIslower(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIslower
):
    """Attribute 'islower' lookup on a str value.

    Typically code like: some_str.islower
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISLOWER"
    attribute_name = "islower"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationIslower(source_ref):
            return ExpressionStrOperationIslower(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationIslower,
            builtin_spec=str_islower_spec,
        )

        return result, "new_expression", "Call to 'islower' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrIslower)


class ExpressionAttributeLookupBytesIslower(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIslower
):
    """Attribute 'islower' lookup on a bytes value.

    Typically code like: some_bytes.islower
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_ISLOWER"
    attribute_name = "islower"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationIslower(source_ref):
            return ExpressionBytesOperationIslower(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationIslower,
            builtin_spec=bytes_islower_spec,
        )

        return result, "new_expression", "Call to 'islower' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesIslower)


class ExpressionAttributeLookupFixedIsnumeric(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isnumeric' of an object.

    Typically code like: source.isnumeric
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISNUMERIC"
    attribute_name = "isnumeric"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is not bytes and subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsnumeric(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isnumeric' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isnumeric",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isnumeric"
        )


attribute_classes["isnumeric"] = ExpressionAttributeLookupFixedIsnumeric


class ExpressionAttributeLookupStrIsnumeric(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsnumeric
):
    """Attribute 'isnumeric' lookup on a str value.

    Typically code like: some_str.isnumeric
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISNUMERIC"
    attribute_name = "isnumeric"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    # No computeExpressionCall as str operation ExpressionStrOperationIsnumeric is not yet implemented


attribute_typed_classes.add(ExpressionAttributeLookupStrIsnumeric)


class ExpressionAttributeLookupFixedIsprintable(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isprintable' of an object.

    Typically code like: source.isprintable
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISPRINTABLE"
    attribute_name = "isprintable"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is not bytes and subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsprintable(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isprintable' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isprintable",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isprintable"
        )


attribute_classes["isprintable"] = ExpressionAttributeLookupFixedIsprintable


class ExpressionAttributeLookupStrIsprintable(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsprintable
):
    """Attribute 'isprintable' lookup on a str value.

    Typically code like: some_str.isprintable
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISPRINTABLE"
    attribute_name = "isprintable"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    # No computeExpressionCall as str operation ExpressionStrOperationIsprintable is not yet implemented


attribute_typed_classes.add(ExpressionAttributeLookupStrIsprintable)


class ExpressionAttributeLookupFixedIsspace(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isspace' of an object.

    Typically code like: source.isspace
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISSPACE"
    attribute_name = "isspace"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsspace(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isspace' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesIsspace(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isspace' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isspace",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isspace"
        )


attribute_classes["isspace"] = ExpressionAttributeLookupFixedIsspace


class ExpressionAttributeLookupStrIsspace(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsspace
):
    """Attribute 'isspace' lookup on a str value.

    Typically code like: some_str.isspace
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISSPACE"
    attribute_name = "isspace"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationIsspace(source_ref):
            return ExpressionStrOperationIsspace(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationIsspace,
            builtin_spec=str_isspace_spec,
        )

        return result, "new_expression", "Call to 'isspace' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrIsspace)


class ExpressionAttributeLookupBytesIsspace(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsspace
):
    """Attribute 'isspace' lookup on a bytes value.

    Typically code like: some_bytes.isspace
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_ISSPACE"
    attribute_name = "isspace"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationIsspace(source_ref):
            return ExpressionBytesOperationIsspace(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationIsspace,
            builtin_spec=bytes_isspace_spec,
        )

        return result, "new_expression", "Call to 'isspace' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesIsspace)


class ExpressionAttributeLookupFixedIstitle(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'istitle' of an object.

    Typically code like: source.istitle
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISTITLE"
    attribute_name = "istitle"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIstitle(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'istitle' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesIstitle(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'istitle' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="istitle",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="istitle"
        )


attribute_classes["istitle"] = ExpressionAttributeLookupFixedIstitle


class ExpressionAttributeLookupStrIstitle(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIstitle
):
    """Attribute 'istitle' lookup on a str value.

    Typically code like: some_str.istitle
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISTITLE"
    attribute_name = "istitle"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationIstitle(source_ref):
            return ExpressionStrOperationIstitle(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationIstitle,
            builtin_spec=str_istitle_spec,
        )

        return result, "new_expression", "Call to 'istitle' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrIstitle)


class ExpressionAttributeLookupBytesIstitle(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIstitle
):
    """Attribute 'istitle' lookup on a bytes value.

    Typically code like: some_bytes.istitle
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_ISTITLE"
    attribute_name = "istitle"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationIstitle(source_ref):
            return ExpressionBytesOperationIstitle(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationIstitle,
            builtin_spec=bytes_istitle_spec,
        )

        return result, "new_expression", "Call to 'istitle' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesIstitle)


class ExpressionAttributeLookupFixedIsupper(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'isupper' of an object.

    Typically code like: source.isupper
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ISUPPER"
    attribute_name = "isupper"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrIsupper(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isupper' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesIsupper(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'isupper' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="isupper",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="isupper"
        )


attribute_classes["isupper"] = ExpressionAttributeLookupFixedIsupper


class ExpressionAttributeLookupStrIsupper(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsupper
):
    """Attribute 'isupper' lookup on a str value.

    Typically code like: some_str.isupper
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ISUPPER"
    attribute_name = "isupper"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationIsupper(source_ref):
            return ExpressionStrOperationIsupper(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationIsupper,
            builtin_spec=str_isupper_spec,
        )

        return result, "new_expression", "Call to 'isupper' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrIsupper)


class ExpressionAttributeLookupBytesIsupper(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIsupper
):
    """Attribute 'isupper' lookup on a bytes value.

    Typically code like: some_bytes.isupper
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_ISUPPER"
    attribute_name = "isupper"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationIsupper(source_ref):
            return ExpressionBytesOperationIsupper(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationIsupper,
            builtin_spec=bytes_isupper_spec,
        )

        return result, "new_expression", "Call to 'isupper' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesIsupper)


class ExpressionAttributeLookupFixedItems(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'items' of an object.

    Typically code like: source.items
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ITEMS"
    attribute_name = "items"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictItems(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'items' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="items",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="items"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["items"] = ExpressionAttributeLookupFixedItems


class ExpressionAttributeLookupDictItems(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedItems
):
    """Attribute 'items' lookup on a dict value.

    Typically code like: some_dict.items
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_ITEMS"
    attribute_name = "items"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationItems(source_ref):
            if str is bytes:
                return ExpressionDictOperationItems(
                    dict_arg=dict_arg, source_ref=source_ref
                )
            else:
                return ExpressionDictOperationIteritems(
                    dict_arg=dict_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationItems,
            builtin_spec=dict_items_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'items' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictItems)


class ExpressionAttributeLookupFixedIteritems(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'iteritems' of an object.

    Typically code like: source.iteritems
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ITERITEMS"
    attribute_name = "iteritems"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is bytes and subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictIteritems(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'iteritems' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="iteritems",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="iteritems"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["iteritems"] = ExpressionAttributeLookupFixedIteritems


class ExpressionAttributeLookupDictIteritems(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIteritems
):
    """Attribute 'iteritems' lookup on a dict value.

    Typically code like: some_dict.iteritems
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_ITERITEMS"
    attribute_name = "iteritems"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationIteritems(source_ref):
            return ExpressionDictOperationIteritems(
                dict_arg=dict_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationIteritems,
            builtin_spec=dict_iteritems_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'iteritems' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictIteritems)


class ExpressionAttributeLookupFixedIterkeys(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'iterkeys' of an object.

    Typically code like: source.iterkeys
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ITERKEYS"
    attribute_name = "iterkeys"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is bytes and subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictIterkeys(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'iterkeys' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="iterkeys",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="iterkeys"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["iterkeys"] = ExpressionAttributeLookupFixedIterkeys


class ExpressionAttributeLookupDictIterkeys(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedIterkeys
):
    """Attribute 'iterkeys' lookup on a dict value.

    Typically code like: some_dict.iterkeys
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_ITERKEYS"
    attribute_name = "iterkeys"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationIterkeys(source_ref):
            return ExpressionDictOperationIterkeys(
                dict_arg=dict_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationIterkeys,
            builtin_spec=dict_iterkeys_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'iterkeys' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictIterkeys)


class ExpressionAttributeLookupFixedItervalues(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'itervalues' of an object.

    Typically code like: source.itervalues
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ITERVALUES"
    attribute_name = "itervalues"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is bytes and subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictItervalues(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'itervalues' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="itervalues",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="itervalues"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["itervalues"] = ExpressionAttributeLookupFixedItervalues


class ExpressionAttributeLookupDictItervalues(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedItervalues
):
    """Attribute 'itervalues' lookup on a dict value.

    Typically code like: some_dict.itervalues
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_ITERVALUES"
    attribute_name = "itervalues"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationItervalues(source_ref):
            return ExpressionDictOperationItervalues(
                dict_arg=dict_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationItervalues,
            builtin_spec=dict_itervalues_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'itervalues' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictItervalues)


class ExpressionAttributeLookupFixedJoin(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'join' of an object.

    Typically code like: source.join
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_JOIN"
    attribute_name = "join"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrJoin(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'join' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesJoin(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'join' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="join",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="join"
        )


attribute_classes["join"] = ExpressionAttributeLookupFixedJoin


class ExpressionAttributeLookupStrJoin(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedJoin
):
    """Attribute 'join' lookup on a str value.

    Typically code like: some_str.join
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_JOIN"
    attribute_name = "join"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationJoin(iterable, source_ref):
            return ExpressionStrOperationJoin(
                str_arg=str_arg, iterable=iterable, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationJoin,
            builtin_spec=str_join_spec,
        )

        return result, "new_expression", "Call to 'join' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrJoin)


class ExpressionAttributeLookupBytesJoin(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedJoin
):
    """Attribute 'join' lookup on a bytes value.

    Typically code like: some_bytes.join
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_JOIN"
    attribute_name = "join"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationJoin(iterable, source_ref):
            return ExpressionBytesOperationJoin(
                bytes_arg=bytes_arg, iterable=iterable, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationJoin,
            builtin_spec=bytes_join_spec,
        )

        return result, "new_expression", "Call to 'join' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesJoin)


class ExpressionAttributeLookupFixedKeys(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'keys' of an object.

    Typically code like: source.keys
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_KEYS"
    attribute_name = "keys"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictKeys(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'keys' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="keys",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="keys"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["keys"] = ExpressionAttributeLookupFixedKeys


class ExpressionAttributeLookupDictKeys(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedKeys
):
    """Attribute 'keys' lookup on a dict value.

    Typically code like: some_dict.keys
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_KEYS"
    attribute_name = "keys"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationKeys(source_ref):
            if str is bytes:
                return ExpressionDictOperationKeys(
                    dict_arg=dict_arg, source_ref=source_ref
                )
            else:
                return ExpressionDictOperationIterkeys(
                    dict_arg=dict_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationKeys,
            builtin_spec=dict_keys_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'keys' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictKeys)


class ExpressionAttributeLookupFixedLjust(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'ljust' of an object.

    Typically code like: source.ljust
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_LJUST"
    attribute_name = "ljust"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrLjust(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'ljust' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesLjust(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'ljust' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="ljust",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="ljust"
        )


attribute_classes["ljust"] = ExpressionAttributeLookupFixedLjust


class ExpressionAttributeLookupStrLjust(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedLjust
):
    """Attribute 'ljust' lookup on a str value.

    Typically code like: some_str.ljust
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_LJUST"
    attribute_name = "ljust"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationLjust(width, fillchar, source_ref):
            if fillchar is not None:
                return ExpressionStrOperationLjust3(
                    str_arg=str_arg,
                    width=width,
                    fillchar=fillchar,
                    source_ref=source_ref,
                )
            else:
                return ExpressionStrOperationLjust2(
                    str_arg=str_arg, width=width, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationLjust,
            builtin_spec=str_ljust_spec,
        )

        return result, "new_expression", "Call to 'ljust' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrLjust)


class ExpressionAttributeLookupBytesLjust(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedLjust
):
    """Attribute 'ljust' lookup on a bytes value.

    Typically code like: some_bytes.ljust
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_LJUST"
    attribute_name = "ljust"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationLjust(width, fillchar, source_ref):
            if fillchar is not None:
                return ExpressionBytesOperationLjust3(
                    bytes_arg=bytes_arg,
                    width=width,
                    fillchar=fillchar,
                    source_ref=source_ref,
                )
            else:
                return ExpressionBytesOperationLjust2(
                    bytes_arg=bytes_arg, width=width, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationLjust,
            builtin_spec=bytes_ljust_spec,
        )

        return result, "new_expression", "Call to 'ljust' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesLjust)


class ExpressionAttributeLookupFixedLower(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'lower' of an object.

    Typically code like: source.lower
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_LOWER"
    attribute_name = "lower"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrLower(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'lower' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesLower(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'lower' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="lower",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="lower"
        )


attribute_classes["lower"] = ExpressionAttributeLookupFixedLower


class ExpressionAttributeLookupStrLower(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedLower
):
    """Attribute 'lower' lookup on a str value.

    Typically code like: some_str.lower
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_LOWER"
    attribute_name = "lower"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationLower(source_ref):
            return ExpressionStrOperationLower(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationLower,
            builtin_spec=str_lower_spec,
        )

        return result, "new_expression", "Call to 'lower' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrLower)


class ExpressionAttributeLookupBytesLower(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedLower
):
    """Attribute 'lower' lookup on a bytes value.

    Typically code like: some_bytes.lower
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_LOWER"
    attribute_name = "lower"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationLower(source_ref):
            return ExpressionBytesOperationLower(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationLower,
            builtin_spec=bytes_lower_spec,
        )

        return result, "new_expression", "Call to 'lower' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesLower)


class ExpressionAttributeLookupFixedLstrip(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'lstrip' of an object.

    Typically code like: source.lstrip
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_LSTRIP"
    attribute_name = "lstrip"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrLstrip(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'lstrip' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesLstrip(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'lstrip' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="lstrip",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="lstrip"
        )


attribute_classes["lstrip"] = ExpressionAttributeLookupFixedLstrip


class ExpressionAttributeLookupStrLstrip(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedLstrip
):
    """Attribute 'lstrip' lookup on a str value.

    Typically code like: some_str.lstrip
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_LSTRIP"
    attribute_name = "lstrip"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationLstrip(chars, source_ref):
            if chars is not None:
                return ExpressionStrOperationLstrip2(
                    str_arg=str_arg, chars=chars, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationLstrip1(
                    str_arg=str_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationLstrip,
            builtin_spec=str_lstrip_spec,
        )

        return result, "new_expression", "Call to 'lstrip' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrLstrip)


class ExpressionAttributeLookupBytesLstrip(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedLstrip
):
    """Attribute 'lstrip' lookup on a bytes value.

    Typically code like: some_bytes.lstrip
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_LSTRIP"
    attribute_name = "lstrip"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationLstrip(chars, source_ref):
            if chars is not None:
                return ExpressionBytesOperationLstrip2(
                    bytes_arg=bytes_arg, chars=chars, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationLstrip1(
                    bytes_arg=bytes_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationLstrip,
            builtin_spec=bytes_lstrip_spec,
        )

        return result, "new_expression", "Call to 'lstrip' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesLstrip)


class ExpressionAttributeLookupFixedMaketrans(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'maketrans' of an object.

    Typically code like: source.maketrans
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_MAKETRANS"
    attribute_name = "maketrans"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is not bytes and subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrMaketrans(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'maketrans' on str shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="maketrans",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="maketrans"
        )


attribute_classes["maketrans"] = ExpressionAttributeLookupFixedMaketrans


class ExpressionAttributeLookupStrMaketrans(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedMaketrans
):
    """Attribute 'maketrans' lookup on a str value.

    Typically code like: some_str.maketrans
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_MAKETRANS"
    attribute_name = "maketrans"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    # No computeExpressionCall as str operation ExpressionStrOperationMaketrans is not yet implemented


attribute_typed_classes.add(ExpressionAttributeLookupStrMaketrans)


class ExpressionAttributeLookupFixedPartition(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'partition' of an object.

    Typically code like: source.partition
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_PARTITION"
    attribute_name = "partition"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrPartition(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'partition' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesPartition(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'partition' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="partition",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="partition"
        )


attribute_classes["partition"] = ExpressionAttributeLookupFixedPartition


class ExpressionAttributeLookupStrPartition(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedPartition
):
    """Attribute 'partition' lookup on a str value.

    Typically code like: some_str.partition
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_PARTITION"
    attribute_name = "partition"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationPartition(sep, source_ref):
            return ExpressionStrOperationPartition(
                str_arg=str_arg, sep=sep, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationPartition,
            builtin_spec=str_partition_spec,
        )

        return result, "new_expression", "Call to 'partition' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrPartition)


class ExpressionAttributeLookupBytesPartition(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedPartition
):
    """Attribute 'partition' lookup on a bytes value.

    Typically code like: some_bytes.partition
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_PARTITION"
    attribute_name = "partition"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationPartition(sep, source_ref):
            return ExpressionBytesOperationPartition(
                bytes_arg=bytes_arg, sep=sep, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationPartition,
            builtin_spec=bytes_partition_spec,
        )

        return result, "new_expression", "Call to 'partition' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesPartition)


class ExpressionAttributeLookupFixedPop(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'pop' of an object.

    Typically code like: source.pop
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_POP"
    attribute_name = "pop"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictPop(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'pop' on dict shape resolved.",
            )
        if subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListPop(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'pop' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="pop",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="pop"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["pop"] = ExpressionAttributeLookupFixedPop


class ExpressionAttributeLookupDictPop(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedPop
):
    """Attribute 'pop' lookup on a dict value.

    Typically code like: some_dict.pop
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_POP"
    attribute_name = "pop"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Might be used to modify the dict.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationPop(key, default, source_ref):
            if default is not None:
                return ExpressionDictOperationPop3(
                    dict_arg=dict_arg, key=key, default=default, source_ref=source_ref
                )
            else:
                return ExpressionDictOperationPop2(
                    dict_arg=dict_arg, key=key, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationPop,
            builtin_spec=dict_pop_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'pop' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictPop)


class ExpressionAttributeLookupListPop(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedPop
):
    """Attribute pop lookup on a list value.

    Typically code like: some_list.pop
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_POP"
    attribute_name = "pop"

    def computeExpression(self, trace_collection):
        # Might be used to modify the list.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationPop(index, source_ref):
            if index is not None:
                return ExpressionListOperationPop2(
                    list_arg=list_arg, index=index, source_ref=source_ref
                )
            else:
                return ExpressionListOperationPop1(
                    list_arg=list_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationPop,
            builtin_spec=list_pop_spec,
        )

        return result, "new_expression", "Call to 'pop' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListPop)


class ExpressionAttributeLookupFixedPopitem(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'popitem' of an object.

    Typically code like: source.popitem
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_POPITEM"
    attribute_name = "popitem"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictPopitem(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'popitem' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="popitem",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="popitem"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["popitem"] = ExpressionAttributeLookupFixedPopitem


class ExpressionAttributeLookupDictPopitem(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedPopitem
):
    """Attribute 'popitem' lookup on a dict value.

    Typically code like: some_dict.popitem
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_POPITEM"
    attribute_name = "popitem"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Might be used to modify the dict.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationPopitem(source_ref):
            return ExpressionDictOperationPopitem(
                dict_arg=dict_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationPopitem,
            builtin_spec=dict_popitem_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'popitem' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictPopitem)


class ExpressionAttributeLookupFixedRemove(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'remove' of an object.

    Typically code like: source.remove
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_REMOVE"
    attribute_name = "remove"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListRemove(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'remove' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="remove",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="remove"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["remove"] = ExpressionAttributeLookupFixedRemove


class ExpressionAttributeLookupListRemove(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRemove
):
    """Attribute remove lookup on a list value.

    Typically code like: some_list.remove
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_REMOVE"
    attribute_name = "remove"

    def computeExpression(self, trace_collection):
        # Might be used to modify the list.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationRemove(value, source_ref):
            return ExpressionListOperationRemove(
                list_arg=list_arg, value=value, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationRemove,
            builtin_spec=list_remove_spec,
        )

        return result, "new_expression", "Call to 'remove' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListRemove)


class ExpressionAttributeLookupFixedReplace(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'replace' of an object.

    Typically code like: source.replace
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_REPLACE"
    attribute_name = "replace"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrReplace(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'replace' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesReplace(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'replace' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="replace",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="replace"
        )


attribute_classes["replace"] = ExpressionAttributeLookupFixedReplace


class ExpressionAttributeLookupStrReplace(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedReplace
):
    """Attribute 'replace' lookup on a str value.

    Typically code like: some_str.replace
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_REPLACE"
    attribute_name = "replace"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationReplace(old, new, count, source_ref):
            if count is not None:
                return ExpressionStrOperationReplace4(
                    str_arg=str_arg,
                    old=old,
                    new=new,
                    count=count,
                    source_ref=source_ref,
                )
            else:
                return ExpressionStrOperationReplace3(
                    str_arg=str_arg, old=old, new=new, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationReplace,
            builtin_spec=str_replace_spec,
        )

        return result, "new_expression", "Call to 'replace' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrReplace)


class ExpressionAttributeLookupBytesReplace(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedReplace
):
    """Attribute 'replace' lookup on a bytes value.

    Typically code like: some_bytes.replace
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_REPLACE"
    attribute_name = "replace"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationReplace(old, new, count, source_ref):
            if count is not None:
                return ExpressionBytesOperationReplace4(
                    bytes_arg=bytes_arg,
                    old=old,
                    new=new,
                    count=count,
                    source_ref=source_ref,
                )
            else:
                return ExpressionBytesOperationReplace3(
                    bytes_arg=bytes_arg, old=old, new=new, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationReplace,
            builtin_spec=bytes_replace_spec,
        )

        return result, "new_expression", "Call to 'replace' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesReplace)


class ExpressionAttributeLookupFixedReverse(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'reverse' of an object.

    Typically code like: source.reverse
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_REVERSE"
    attribute_name = "reverse"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListReverse(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'reverse' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="reverse",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="reverse"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["reverse"] = ExpressionAttributeLookupFixedReverse


class ExpressionAttributeLookupListReverse(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedReverse
):
    """Attribute reverse lookup on a list value.

    Typically code like: some_list.reverse
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_REVERSE"
    attribute_name = "reverse"

    def computeExpression(self, trace_collection):
        # Might be used to modify the list.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, list_arg, trace_collection):
        def wrapExpressionListOperationReverse(source_ref):
            return ExpressionListOperationReverse(
                list_arg=list_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionListOperationReverse,
            builtin_spec=list_reverse_spec,
        )

        return result, "new_expression", "Call to 'reverse' of list recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        list_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, list_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupListReverse)


class ExpressionAttributeLookupFixedRfind(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'rfind' of an object.

    Typically code like: source.rfind
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_RFIND"
    attribute_name = "rfind"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrRfind(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rfind' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesRfind(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rfind' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="rfind",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="rfind"
        )


attribute_classes["rfind"] = ExpressionAttributeLookupFixedRfind


class ExpressionAttributeLookupStrRfind(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRfind
):
    """Attribute 'rfind' lookup on a str value.

    Typically code like: some_str.rfind
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_RFIND"
    attribute_name = "rfind"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationRfind(sub, start, end, source_ref):
            if end is not None:
                return ExpressionStrOperationRfind4(
                    str_arg=str_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionStrOperationRfind3(
                    str_arg=str_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationRfind2(
                    str_arg=str_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationRfind,
            builtin_spec=str_rfind_spec,
        )

        return result, "new_expression", "Call to 'rfind' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrRfind)


class ExpressionAttributeLookupBytesRfind(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRfind
):
    """Attribute 'rfind' lookup on a bytes value.

    Typically code like: some_bytes.rfind
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_RFIND"
    attribute_name = "rfind"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationRfind(sub, start, end, source_ref):
            if end is not None:
                return ExpressionBytesOperationRfind4(
                    bytes_arg=bytes_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionBytesOperationRfind3(
                    bytes_arg=bytes_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationRfind2(
                    bytes_arg=bytes_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationRfind,
            builtin_spec=bytes_rfind_spec,
        )

        return result, "new_expression", "Call to 'rfind' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesRfind)


class ExpressionAttributeLookupFixedRindex(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'rindex' of an object.

    Typically code like: source.rindex
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_RINDEX"
    attribute_name = "rindex"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrRindex(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rindex' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesRindex(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rindex' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="rindex",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="rindex"
        )


attribute_classes["rindex"] = ExpressionAttributeLookupFixedRindex


class ExpressionAttributeLookupStrRindex(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRindex
):
    """Attribute 'rindex' lookup on a str value.

    Typically code like: some_str.rindex
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_RINDEX"
    attribute_name = "rindex"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationRindex(sub, start, end, source_ref):
            if end is not None:
                return ExpressionStrOperationRindex4(
                    str_arg=str_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionStrOperationRindex3(
                    str_arg=str_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationRindex2(
                    str_arg=str_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationRindex,
            builtin_spec=str_rindex_spec,
        )

        return result, "new_expression", "Call to 'rindex' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrRindex)


class ExpressionAttributeLookupBytesRindex(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRindex
):
    """Attribute 'rindex' lookup on a bytes value.

    Typically code like: some_bytes.rindex
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_RINDEX"
    attribute_name = "rindex"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationRindex(sub, start, end, source_ref):
            if end is not None:
                return ExpressionBytesOperationRindex4(
                    bytes_arg=bytes_arg,
                    sub=sub,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionBytesOperationRindex3(
                    bytes_arg=bytes_arg, sub=sub, start=start, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationRindex2(
                    bytes_arg=bytes_arg, sub=sub, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationRindex,
            builtin_spec=bytes_rindex_spec,
        )

        return result, "new_expression", "Call to 'rindex' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesRindex)


class ExpressionAttributeLookupFixedRjust(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'rjust' of an object.

    Typically code like: source.rjust
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_RJUST"
    attribute_name = "rjust"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrRjust(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rjust' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesRjust(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rjust' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="rjust",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="rjust"
        )


attribute_classes["rjust"] = ExpressionAttributeLookupFixedRjust


class ExpressionAttributeLookupStrRjust(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRjust
):
    """Attribute 'rjust' lookup on a str value.

    Typically code like: some_str.rjust
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_RJUST"
    attribute_name = "rjust"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationRjust(width, fillchar, source_ref):
            if fillchar is not None:
                return ExpressionStrOperationRjust3(
                    str_arg=str_arg,
                    width=width,
                    fillchar=fillchar,
                    source_ref=source_ref,
                )
            else:
                return ExpressionStrOperationRjust2(
                    str_arg=str_arg, width=width, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationRjust,
            builtin_spec=str_rjust_spec,
        )

        return result, "new_expression", "Call to 'rjust' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrRjust)


class ExpressionAttributeLookupBytesRjust(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRjust
):
    """Attribute 'rjust' lookup on a bytes value.

    Typically code like: some_bytes.rjust
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_RJUST"
    attribute_name = "rjust"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationRjust(width, fillchar, source_ref):
            if fillchar is not None:
                return ExpressionBytesOperationRjust3(
                    bytes_arg=bytes_arg,
                    width=width,
                    fillchar=fillchar,
                    source_ref=source_ref,
                )
            else:
                return ExpressionBytesOperationRjust2(
                    bytes_arg=bytes_arg, width=width, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationRjust,
            builtin_spec=bytes_rjust_spec,
        )

        return result, "new_expression", "Call to 'rjust' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesRjust)


class ExpressionAttributeLookupFixedRpartition(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'rpartition' of an object.

    Typically code like: source.rpartition
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_RPARTITION"
    attribute_name = "rpartition"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrRpartition(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rpartition' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesRpartition(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rpartition' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="rpartition",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="rpartition"
        )


attribute_classes["rpartition"] = ExpressionAttributeLookupFixedRpartition


class ExpressionAttributeLookupStrRpartition(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRpartition
):
    """Attribute 'rpartition' lookup on a str value.

    Typically code like: some_str.rpartition
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_RPARTITION"
    attribute_name = "rpartition"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationRpartition(sep, source_ref):
            return ExpressionStrOperationRpartition(
                str_arg=str_arg, sep=sep, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationRpartition,
            builtin_spec=str_rpartition_spec,
        )

        return result, "new_expression", "Call to 'rpartition' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrRpartition)


class ExpressionAttributeLookupBytesRpartition(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRpartition
):
    """Attribute 'rpartition' lookup on a bytes value.

    Typically code like: some_bytes.rpartition
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_RPARTITION"
    attribute_name = "rpartition"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationRpartition(sep, source_ref):
            return ExpressionBytesOperationRpartition(
                bytes_arg=bytes_arg, sep=sep, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationRpartition,
            builtin_spec=bytes_rpartition_spec,
        )

        return result, "new_expression", "Call to 'rpartition' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesRpartition)


class ExpressionAttributeLookupFixedRsplit(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'rsplit' of an object.

    Typically code like: source.rsplit
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_RSPLIT"
    attribute_name = "rsplit"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrRsplit(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rsplit' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesRsplit(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rsplit' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="rsplit",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="rsplit"
        )


attribute_classes["rsplit"] = ExpressionAttributeLookupFixedRsplit


class ExpressionAttributeLookupStrRsplit(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRsplit
):
    """Attribute 'rsplit' lookup on a str value.

    Typically code like: some_str.rsplit
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_RSPLIT"
    attribute_name = "rsplit"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationRsplit(sep, maxsplit, source_ref):
            if maxsplit is not None:
                return makeExpressionStrOperationRsplit3(
                    str_arg=str_arg, sep=sep, maxsplit=maxsplit, source_ref=source_ref
                )
            elif sep is not None:
                return ExpressionStrOperationRsplit2(
                    str_arg=str_arg, sep=sep, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationRsplit1(
                    str_arg=str_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationRsplit,
            builtin_spec=str_rsplit_spec,
        )

        return result, "new_expression", "Call to 'rsplit' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrRsplit)


class ExpressionAttributeLookupBytesRsplit(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRsplit
):
    """Attribute 'rsplit' lookup on a bytes value.

    Typically code like: some_bytes.rsplit
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_RSPLIT"
    attribute_name = "rsplit"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationRsplit(sep, maxsplit, source_ref):
            if maxsplit is not None:
                return makeExpressionBytesOperationRsplit3(
                    bytes_arg=bytes_arg,
                    sep=sep,
                    maxsplit=maxsplit,
                    source_ref=source_ref,
                )
            elif sep is not None:
                return ExpressionBytesOperationRsplit2(
                    bytes_arg=bytes_arg, sep=sep, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationRsplit1(
                    bytes_arg=bytes_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationRsplit,
            builtin_spec=bytes_rsplit_spec,
        )

        return result, "new_expression", "Call to 'rsplit' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesRsplit)


class ExpressionAttributeLookupFixedRstrip(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'rstrip' of an object.

    Typically code like: source.rstrip
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_RSTRIP"
    attribute_name = "rstrip"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrRstrip(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rstrip' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesRstrip(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'rstrip' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="rstrip",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="rstrip"
        )


attribute_classes["rstrip"] = ExpressionAttributeLookupFixedRstrip


class ExpressionAttributeLookupStrRstrip(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRstrip
):
    """Attribute 'rstrip' lookup on a str value.

    Typically code like: some_str.rstrip
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_RSTRIP"
    attribute_name = "rstrip"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationRstrip(chars, source_ref):
            if chars is not None:
                return ExpressionStrOperationRstrip2(
                    str_arg=str_arg, chars=chars, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationRstrip1(
                    str_arg=str_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationRstrip,
            builtin_spec=str_rstrip_spec,
        )

        return result, "new_expression", "Call to 'rstrip' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrRstrip)


class ExpressionAttributeLookupBytesRstrip(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedRstrip
):
    """Attribute 'rstrip' lookup on a bytes value.

    Typically code like: some_bytes.rstrip
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_RSTRIP"
    attribute_name = "rstrip"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationRstrip(chars, source_ref):
            if chars is not None:
                return ExpressionBytesOperationRstrip2(
                    bytes_arg=bytes_arg, chars=chars, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationRstrip1(
                    bytes_arg=bytes_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationRstrip,
            builtin_spec=bytes_rstrip_spec,
        )

        return result, "new_expression", "Call to 'rstrip' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesRstrip)


class ExpressionAttributeLookupFixedSetdefault(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'setdefault' of an object.

    Typically code like: source.setdefault
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_SETDEFAULT"
    attribute_name = "setdefault"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictSetdefault(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'setdefault' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="setdefault",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="setdefault"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["setdefault"] = ExpressionAttributeLookupFixedSetdefault


class ExpressionAttributeLookupDictSetdefault(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedSetdefault
):
    """Attribute 'setdefault' lookup on a dict value.

    Typically code like: some_dict.setdefault
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_SETDEFAULT"
    attribute_name = "setdefault"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Might be used to modify the dict.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationSetdefault(key, default, source_ref):
            if default is not None:
                return ExpressionDictOperationSetdefault3(
                    dict_arg=dict_arg, key=key, default=default, source_ref=source_ref
                )
            else:
                return ExpressionDictOperationSetdefault2(
                    dict_arg=dict_arg, key=key, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationSetdefault,
            builtin_spec=dict_setdefault_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'setdefault' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictSetdefault)


class ExpressionAttributeLookupFixedSort(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'sort' of an object.

    Typically code like: source.sort
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_SORT"
    attribute_name = "sort"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeListExact():
            result = ExpressionAttributeLookupListSort(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'sort' on list shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="sort",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="sort"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["sort"] = ExpressionAttributeLookupFixedSort


class ExpressionAttributeLookupListSort(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedSort
):
    """Attribute sort lookup on a list value.

    Typically code like: some_list.sort
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_LIST_SORT"
    attribute_name = "sort"

    def computeExpression(self, trace_collection):
        # Might be used to modify the list.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    # No computeExpressionCall as list operation ExpressionListOperationSort is not yet implemented


attribute_typed_classes.add(ExpressionAttributeLookupListSort)


class ExpressionAttributeLookupFixedSplit(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'split' of an object.

    Typically code like: source.split
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_SPLIT"
    attribute_name = "split"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrSplit(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'split' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesSplit(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'split' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="split",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="split"
        )


attribute_classes["split"] = ExpressionAttributeLookupFixedSplit


class ExpressionAttributeLookupStrSplit(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedSplit
):
    """Attribute 'split' lookup on a str value.

    Typically code like: some_str.split
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_SPLIT"
    attribute_name = "split"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationSplit(sep, maxsplit, source_ref):
            if maxsplit is not None:
                return makeExpressionStrOperationSplit3(
                    str_arg=str_arg, sep=sep, maxsplit=maxsplit, source_ref=source_ref
                )
            elif sep is not None:
                return ExpressionStrOperationSplit2(
                    str_arg=str_arg, sep=sep, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationSplit1(
                    str_arg=str_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationSplit,
            builtin_spec=str_split_spec,
        )

        return result, "new_expression", "Call to 'split' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrSplit)


class ExpressionAttributeLookupBytesSplit(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedSplit
):
    """Attribute 'split' lookup on a bytes value.

    Typically code like: some_bytes.split
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_SPLIT"
    attribute_name = "split"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationSplit(sep, maxsplit, source_ref):
            if maxsplit is not None:
                return makeExpressionBytesOperationSplit3(
                    bytes_arg=bytes_arg,
                    sep=sep,
                    maxsplit=maxsplit,
                    source_ref=source_ref,
                )
            elif sep is not None:
                return ExpressionBytesOperationSplit2(
                    bytes_arg=bytes_arg, sep=sep, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationSplit1(
                    bytes_arg=bytes_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationSplit,
            builtin_spec=bytes_split_spec,
        )

        return result, "new_expression", "Call to 'split' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesSplit)


class ExpressionAttributeLookupFixedSplitlines(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'splitlines' of an object.

    Typically code like: source.splitlines
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_SPLITLINES"
    attribute_name = "splitlines"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrSplitlines(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'splitlines' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesSplitlines(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'splitlines' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="splitlines",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="splitlines"
        )


attribute_classes["splitlines"] = ExpressionAttributeLookupFixedSplitlines


class ExpressionAttributeLookupStrSplitlines(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedSplitlines
):
    """Attribute 'splitlines' lookup on a str value.

    Typically code like: some_str.splitlines
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_SPLITLINES"
    attribute_name = "splitlines"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationSplitlines(keepends, source_ref):
            if keepends is not None:
                return ExpressionStrOperationSplitlines2(
                    str_arg=str_arg, keepends=keepends, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationSplitlines1(
                    str_arg=str_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationSplitlines,
            builtin_spec=str_splitlines_spec,
        )

        return result, "new_expression", "Call to 'splitlines' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrSplitlines)


class ExpressionAttributeLookupBytesSplitlines(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedSplitlines
):
    """Attribute 'splitlines' lookup on a bytes value.

    Typically code like: some_bytes.splitlines
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_SPLITLINES"
    attribute_name = "splitlines"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationSplitlines(keepends, source_ref):
            if keepends is not None:
                return ExpressionBytesOperationSplitlines2(
                    bytes_arg=bytes_arg, keepends=keepends, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationSplitlines1(
                    bytes_arg=bytes_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationSplitlines,
            builtin_spec=bytes_splitlines_spec,
        )

        return result, "new_expression", "Call to 'splitlines' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesSplitlines)


class ExpressionAttributeLookupFixedStartswith(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'startswith' of an object.

    Typically code like: source.startswith
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_STARTSWITH"
    attribute_name = "startswith"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrStartswith(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'startswith' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesStartswith(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'startswith' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="startswith",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="startswith"
        )


attribute_classes["startswith"] = ExpressionAttributeLookupFixedStartswith


class ExpressionAttributeLookupStrStartswith(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedStartswith
):
    """Attribute 'startswith' lookup on a str value.

    Typically code like: some_str.startswith
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_STARTSWITH"
    attribute_name = "startswith"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationStartswith(prefix, start, end, source_ref):
            if end is not None:
                return ExpressionStrOperationStartswith4(
                    str_arg=str_arg,
                    prefix=prefix,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionStrOperationStartswith3(
                    str_arg=str_arg, prefix=prefix, start=start, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationStartswith2(
                    str_arg=str_arg, prefix=prefix, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationStartswith,
            builtin_spec=str_startswith_spec,
        )

        return result, "new_expression", "Call to 'startswith' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrStartswith)


class ExpressionAttributeLookupBytesStartswith(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedStartswith
):
    """Attribute 'startswith' lookup on a bytes value.

    Typically code like: some_bytes.startswith
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_STARTSWITH"
    attribute_name = "startswith"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationStartswith(prefix, start, end, source_ref):
            if end is not None:
                return ExpressionBytesOperationStartswith4(
                    bytes_arg=bytes_arg,
                    prefix=prefix,
                    start=start,
                    end=end,
                    source_ref=source_ref,
                )
            elif start is not None:
                return ExpressionBytesOperationStartswith3(
                    bytes_arg=bytes_arg,
                    prefix=prefix,
                    start=start,
                    source_ref=source_ref,
                )
            else:
                return ExpressionBytesOperationStartswith2(
                    bytes_arg=bytes_arg, prefix=prefix, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationStartswith,
            builtin_spec=bytes_startswith_spec,
        )

        return result, "new_expression", "Call to 'startswith' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesStartswith)


class ExpressionAttributeLookupFixedStrip(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'strip' of an object.

    Typically code like: source.strip
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_STRIP"
    attribute_name = "strip"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrStrip(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'strip' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesStrip(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'strip' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="strip",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="strip"
        )


attribute_classes["strip"] = ExpressionAttributeLookupFixedStrip


class ExpressionAttributeLookupStrStrip(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedStrip
):
    """Attribute 'strip' lookup on a str value.

    Typically code like: some_str.strip
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_STRIP"
    attribute_name = "strip"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationStrip(chars, source_ref):
            if chars is not None:
                return ExpressionStrOperationStrip2(
                    str_arg=str_arg, chars=chars, source_ref=source_ref
                )
            else:
                return ExpressionStrOperationStrip1(
                    str_arg=str_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationStrip,
            builtin_spec=str_strip_spec,
        )

        return result, "new_expression", "Call to 'strip' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrStrip)


class ExpressionAttributeLookupBytesStrip(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedStrip
):
    """Attribute 'strip' lookup on a bytes value.

    Typically code like: some_bytes.strip
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_STRIP"
    attribute_name = "strip"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationStrip(chars, source_ref):
            if chars is not None:
                return ExpressionBytesOperationStrip2(
                    bytes_arg=bytes_arg, chars=chars, source_ref=source_ref
                )
            else:
                return ExpressionBytesOperationStrip1(
                    bytes_arg=bytes_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationStrip,
            builtin_spec=bytes_strip_spec,
        )

        return result, "new_expression", "Call to 'strip' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesStrip)


class ExpressionAttributeLookupFixedSwapcase(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'swapcase' of an object.

    Typically code like: source.swapcase
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_SWAPCASE"
    attribute_name = "swapcase"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrSwapcase(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'swapcase' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesSwapcase(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'swapcase' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="swapcase",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="swapcase"
        )


attribute_classes["swapcase"] = ExpressionAttributeLookupFixedSwapcase


class ExpressionAttributeLookupStrSwapcase(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedSwapcase
):
    """Attribute 'swapcase' lookup on a str value.

    Typically code like: some_str.swapcase
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_SWAPCASE"
    attribute_name = "swapcase"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationSwapcase(source_ref):
            return ExpressionStrOperationSwapcase(
                str_arg=str_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationSwapcase,
            builtin_spec=str_swapcase_spec,
        )

        return result, "new_expression", "Call to 'swapcase' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrSwapcase)


class ExpressionAttributeLookupBytesSwapcase(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedSwapcase
):
    """Attribute 'swapcase' lookup on a bytes value.

    Typically code like: some_bytes.swapcase
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_SWAPCASE"
    attribute_name = "swapcase"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationSwapcase(source_ref):
            return ExpressionBytesOperationSwapcase(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationSwapcase,
            builtin_spec=bytes_swapcase_spec,
        )

        return result, "new_expression", "Call to 'swapcase' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesSwapcase)


class ExpressionAttributeLookupFixedTitle(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'title' of an object.

    Typically code like: source.title
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_TITLE"
    attribute_name = "title"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrTitle(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'title' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesTitle(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'title' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="title",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="title"
        )


attribute_classes["title"] = ExpressionAttributeLookupFixedTitle


class ExpressionAttributeLookupStrTitle(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedTitle
):
    """Attribute 'title' lookup on a str value.

    Typically code like: some_str.title
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_TITLE"
    attribute_name = "title"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationTitle(source_ref):
            return ExpressionStrOperationTitle(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationTitle,
            builtin_spec=str_title_spec,
        )

        return result, "new_expression", "Call to 'title' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrTitle)


class ExpressionAttributeLookupBytesTitle(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedTitle
):
    """Attribute 'title' lookup on a bytes value.

    Typically code like: some_bytes.title
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_TITLE"
    attribute_name = "title"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationTitle(source_ref):
            return ExpressionBytesOperationTitle(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationTitle,
            builtin_spec=bytes_title_spec,
        )

        return result, "new_expression", "Call to 'title' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesTitle)


class ExpressionAttributeLookupFixedTranslate(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'translate' of an object.

    Typically code like: source.translate
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_TRANSLATE"
    attribute_name = "translate"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrTranslate(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'translate' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesTranslate(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'translate' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="translate",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="translate"
        )


attribute_classes["translate"] = ExpressionAttributeLookupFixedTranslate


class ExpressionAttributeLookupStrTranslate(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedTranslate
):
    """Attribute 'translate' lookup on a str value.

    Typically code like: some_str.translate
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_TRANSLATE"
    attribute_name = "translate"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationTranslate(table, source_ref):
            return ExpressionStrOperationTranslate(
                str_arg=str_arg, table=table, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationTranslate,
            builtin_spec=str_translate_spec,
        )

        return result, "new_expression", "Call to 'translate' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrTranslate)


class ExpressionAttributeLookupBytesTranslate(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedTranslate
):
    """Attribute 'translate' lookup on a bytes value.

    Typically code like: some_bytes.translate
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_TRANSLATE"
    attribute_name = "translate"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationTranslate(table, delete, source_ref):
            if delete is not None:
                return ExpressionBytesOperationTranslate3(
                    bytes_arg=bytes_arg,
                    table=table,
                    delete=delete,
                    source_ref=source_ref,
                )
            else:
                return ExpressionBytesOperationTranslate2(
                    bytes_arg=bytes_arg, table=table, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationTranslate,
            builtin_spec=bytes_translate_spec,
        )

        return result, "new_expression", "Call to 'translate' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesTranslate)


class ExpressionAttributeLookupFixedUpdate(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'update' of an object.

    Typically code like: source.update
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_UPDATE"
    attribute_name = "update"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictUpdate(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'update' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="update",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="update"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["update"] = ExpressionAttributeLookupFixedUpdate


class ExpressionAttributeLookupDictUpdate(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedUpdate
):
    """Attribute 'update' lookup on a dict value.

    Typically code like: some_dict.update
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_UPDATE"
    attribute_name = "update"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Might be used to modify the dict.
        trace_collection.removeKnowledge(self.subnode_expression)

        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationUpdate(iterable, pairs, source_ref):
            if pairs:
                return makeExpressionDictOperationUpdate3(
                    dict_arg=dict_arg,
                    iterable=iterable,
                    pairs=makeKeyValuePairExpressionsFromKwArgs(pairs),
                    source_ref=source_ref,
                )
            else:
                return ExpressionDictOperationUpdate2(
                    dict_arg=dict_arg, iterable=iterable, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationUpdate,
            builtin_spec=dict_update_spec,
            empty_special_class=lambda source_ref: wrapExpressionWithNodeSideEffects(
                new_node=makeConstantRefNode(constant=None, source_ref=source_ref),
                old_node=dict_arg,
            ),
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'update' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictUpdate)


class ExpressionAttributeLookupFixedUpper(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'upper' of an object.

    Typically code like: source.upper
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_UPPER"
    attribute_name = "upper"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrUpper(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'upper' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesUpper(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'upper' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="upper",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="upper"
        )


attribute_classes["upper"] = ExpressionAttributeLookupFixedUpper


class ExpressionAttributeLookupStrUpper(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedUpper
):
    """Attribute 'upper' lookup on a str value.

    Typically code like: some_str.upper
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_UPPER"
    attribute_name = "upper"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationUpper(source_ref):
            return ExpressionStrOperationUpper(str_arg=str_arg, source_ref=source_ref)

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationUpper,
            builtin_spec=str_upper_spec,
        )

        return result, "new_expression", "Call to 'upper' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrUpper)


class ExpressionAttributeLookupBytesUpper(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedUpper
):
    """Attribute 'upper' lookup on a bytes value.

    Typically code like: some_bytes.upper
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_UPPER"
    attribute_name = "upper"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationUpper(source_ref):
            return ExpressionBytesOperationUpper(
                bytes_arg=bytes_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationUpper,
            builtin_spec=bytes_upper_spec,
        )

        return result, "new_expression", "Call to 'upper' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesUpper)


class ExpressionAttributeLookupFixedValues(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'values' of an object.

    Typically code like: source.values
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_VALUES"
    attribute_name = "values"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictValues(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'values' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="values",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="values"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["values"] = ExpressionAttributeLookupFixedValues


class ExpressionAttributeLookupDictValues(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedValues
):
    """Attribute 'values' lookup on a dict value.

    Typically code like: some_dict.values
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_VALUES"
    attribute_name = "values"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationValues(source_ref):
            if str is bytes:
                return ExpressionDictOperationValues(
                    dict_arg=dict_arg, source_ref=source_ref
                )
            else:
                return ExpressionDictOperationItervalues(
                    dict_arg=dict_arg, source_ref=source_ref
                )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationValues,
            builtin_spec=dict_values_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'values' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictValues)


class ExpressionAttributeLookupFixedViewitems(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'viewitems' of an object.

    Typically code like: source.viewitems
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_VIEWITEMS"
    attribute_name = "viewitems"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is bytes and subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictViewitems(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'viewitems' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="viewitems",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="viewitems"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["viewitems"] = ExpressionAttributeLookupFixedViewitems


class ExpressionAttributeLookupDictViewitems(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedViewitems
):
    """Attribute 'viewitems' lookup on a dict value.

    Typically code like: some_dict.viewitems
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_VIEWITEMS"
    attribute_name = "viewitems"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationViewitems(source_ref):
            return ExpressionDictOperationViewitems(
                dict_arg=dict_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationViewitems,
            builtin_spec=dict_viewitems_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'viewitems' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictViewitems)


class ExpressionAttributeLookupFixedViewkeys(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'viewkeys' of an object.

    Typically code like: source.viewkeys
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_VIEWKEYS"
    attribute_name = "viewkeys"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is bytes and subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictViewkeys(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'viewkeys' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="viewkeys",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="viewkeys"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["viewkeys"] = ExpressionAttributeLookupFixedViewkeys


class ExpressionAttributeLookupDictViewkeys(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedViewkeys
):
    """Attribute 'viewkeys' lookup on a dict value.

    Typically code like: some_dict.viewkeys
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_VIEWKEYS"
    attribute_name = "viewkeys"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationViewkeys(source_ref):
            return ExpressionDictOperationViewkeys(
                dict_arg=dict_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationViewkeys,
            builtin_spec=dict_viewkeys_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'viewkeys' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictViewkeys)


class ExpressionAttributeLookupFixedViewvalues(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'viewvalues' of an object.

    Typically code like: source.viewvalues
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_VIEWVALUES"
    attribute_name = "viewvalues"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if str is bytes and subnode_expression.hasShapeDictionaryExact():
            return trace_collection.computedExpressionResult(
                expression=ExpressionAttributeLookupDictViewvalues(
                    expression=subnode_expression, source_ref=self.source_ref
                ),
                change_tags="new_expression",
                change_desc="Attribute lookup 'viewvalues' on dict shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="viewvalues",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="viewvalues"
        )

    def onContentEscapes(self, trace_collection):
        self.subnode_expression.onContentEscapes(trace_collection)


attribute_classes["viewvalues"] = ExpressionAttributeLookupFixedViewvalues


class ExpressionAttributeLookupDictViewvalues(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedViewvalues
):
    """Attribute 'viewvalues' lookup on a dict value.

    Typically code like: some_dict.viewvalues
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_DICT_VIEWVALUES"
    attribute_name = "viewvalues"

    # There is nothing to compute for it as a value.
    # TODO: Enable this once we can also say removal of knowable for an argument.
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        # Cannot be used to modify the dict.
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, dict_arg, trace_collection):
        def wrapExpressionDictOperationViewvalues(source_ref):
            return ExpressionDictOperationViewvalues(
                dict_arg=dict_arg, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        # Make sure we wait with knowing if the content is safe to use until its time.
        call_node.onContentEscapes(trace_collection)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionDictOperationViewvalues,
            builtin_spec=dict_viewvalues_spec,
        )

        return trace_collection.computedExpressionResult(
            expression=result,
            change_tags="new_expression",
            change_desc="Call to 'viewvalues' of dictionary recognized.",
        )

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        dict_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, dict_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupDictViewvalues)


class ExpressionAttributeLookupFixedZfill(ExpressionAttributeLookupFixedBase):
    """Looking up an attribute value 'zfill' of an object.

    Typically code like: source.zfill
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_FIXED_ZFILL"
    attribute_name = "zfill"

    def computeExpression(self, trace_collection):
        subnode_expression = self.subnode_expression

        if subnode_expression.hasShapeStrExact():
            result = ExpressionAttributeLookupStrZfill(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'zfill' on str shape resolved.",
            )
        if str is not bytes and subnode_expression.hasShapeBytesExact():
            result = ExpressionAttributeLookupBytesZfill(
                expression=subnode_expression, source_ref=self.source_ref
            )

            return trace_collection.computedExpressionResult(
                expression=result,
                change_tags="new_expression",
                change_desc="Attribute lookup 'zfill' on bytes shape resolved.",
            )

        return subnode_expression.computeExpressionAttribute(
            lookup_node=self,
            attribute_name="zfill",
            trace_collection=trace_collection,
        )

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseExceptionAttributeLookup(
            exception_type=exception_type, attribute_name="zfill"
        )


attribute_classes["zfill"] = ExpressionAttributeLookupFixedZfill


class ExpressionAttributeLookupStrZfill(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedZfill
):
    """Attribute 'zfill' lookup on a str value.

    Typically code like: some_str.zfill
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_STR_ZFILL"
    attribute_name = "zfill"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, str_arg, trace_collection):
        def wrapExpressionStrOperationZfill(width, source_ref):
            return ExpressionStrOperationZfill(
                str_arg=str_arg, width=width, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionStrOperationZfill,
            builtin_spec=str_zfill_spec,
        )

        return result, "new_expression", "Call to 'zfill' of str recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        str_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, str_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupStrZfill)


class ExpressionAttributeLookupBytesZfill(
    SideEffectsFromChildrenMixin, ExpressionAttributeLookupFixedZfill
):
    """Attribute 'zfill' lookup on a bytes value.

    Typically code like: some_bytes.zfill
    """

    kind = "EXPRESSION_ATTRIBUTE_LOOKUP_BYTES_ZFILL"
    attribute_name = "zfill"

    # There is nothing to compute for it as a value.
    # TODO: Enable this
    # auto_compute_handling = "final,no_raise"

    def computeExpression(self, trace_collection):
        return self, None, None

    @staticmethod
    def _computeExpressionCall(call_node, bytes_arg, trace_collection):
        def wrapExpressionBytesOperationZfill(width, source_ref):
            return ExpressionBytesOperationZfill(
                bytes_arg=bytes_arg, width=width, source_ref=source_ref
            )

        # Anything may happen. On next pass, if replaced, we might be better
        # but not now.
        trace_collection.onExceptionRaiseExit(BaseException)

        result = extractBuiltinArgs(
            node=call_node,
            builtin_class=wrapExpressionBytesOperationZfill,
            builtin_spec=bytes_zfill_spec,
        )

        return result, "new_expression", "Call to 'zfill' of bytes recognized."

    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
        return self._computeExpressionCall(
            call_node, self.subnode_expression, trace_collection
        )

    def computeExpressionCallViaVariable(
        self, call_node, variable_ref_node, call_args, call_kw, trace_collection
    ):
        bytes_node = makeExpressionAttributeLookup(
            expression=variable_ref_node,
            attribute_name="__self__",
            # TODO: Would be nice to have the real source reference here, but it feels
            # a bit expensive.
            source_ref=variable_ref_node.source_ref,
        )

        return self._computeExpressionCall(call_node, bytes_node, trace_collection)

    def mayRaiseException(self, exception_type):
        return self.subnode_expression.mayRaiseException(exception_type)


attribute_typed_classes.add(ExpressionAttributeLookupBytesZfill)

#     Part of "Nuitka", an optimizing Python compiler that is compatible and
#     integrates with CPython, but also works on its own.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
